#include <iostream>
#include <Eigen/Dense>
#include <Tim_STL.h>
#include "MLP.h"
#include "MLP.cpp"

using namespace std;
#define print(x) cout<<(x)<<endl
/*extern double ReLU(double x);
extern double MSE_Loss(Eigen::VectorXd const& y, Eigen::VectorXd const& y_hat);*/

void eigen_test1()
{
	Eigen::VectorXd now(3);
	now(1) = 1.0;
	Eigen::MatrixXd now2 {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}};
	Eigen::MatrixXd now3 = Eigen::MatrixXd::Random(3, 3);

	//print(now3);
	//cout<<now.transpose() * now2;
	print(now2.unaryExpr([&](double d) {return d / 4;}));
}

void eigen_test2()
{
	Eigen::VectorXd now(3);
	now(1) = 1.0;
	Eigen::MatrixXd now2 {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}, {7.0, 8.0, 9.0}};
	Eigen::MatrixXd now3 = Eigen::MatrixXd::Random(3, 3);

	print(now2.unaryExpr([&](double d) {return d / 4;}));
	print(now2.binaryExpr(now2, [&](double d1, double d2) { return d1 * d2;}));

	return;
}

void layer_test()
{
	Layer myL(5, 4, ReLU, NULL);
	print(myL(Eigen::VectorXd::Random(5)));

	return;
}

void forward_test()
{
	int a[5] = {16, 8, 4, 2, 1};
	MLP myMLP(a, 5);
	print(myMLP.forward(Eigen::VectorXd::Random(16)));
	print(myMLP);
}

class int_s
{
	public:
		void operator()(int& x) {x = x * x;};
};

void test_attr()
{
	tim::list<int> my;
	for (int i = 1; i < 10; i++) my.insert_back(i);
	int_s my_s;
	my.reverse_traverse<int_s>(my_s);
	for (int i = 0; i < my.size(); i++)
		print(my[i]);

	return;
}

void double_eq()
{
	double a = 0.0;
	print(0.0001 / 0.000001754);
	int x = (1 + x);
}

void sgd_test()
{
	int a[3] {4, 2, 1};
	MLP myMLP(a, 3);
	SGD mySGD(0.001, 0, LOSS);

	//std::cout<<myMLP;
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	std::cout<<myMLP.step<SGD>(mySGD, LOSS, Eigen::VectorXd::Random(4), Eigen::VectorXd::Random(1));
	//std::cout<<myMLP;
}

Eigen::VectorXd fuc_65472(Eigen::VectorXd const& x)
{
	double y =  6 * pow(x[0], 6) + 5 * pow(x[1], 5) + 4 * pow(x[2], 4) + 7 * pow(x[3], 7) + 2 * pow(x[4], 2);
	Eigen::VectorXd Y = Eigen::VectorXd::Random(1);
	Y[0] = y;
	return Y;
}

Eigen::VectorXd fuc_two(Eigen::VectorXd const& x)
{
	double y =  4 * x[0] - 3 * x[1] + 2 * x[2] + 10;
	Eigen::VectorXd Y = Eigen::VectorXd::Random(1);
	Y[0] = y;
	return Y;
}

void verify_general_approach()
{
	int shape[8] {5, 5, 5, 5, 5, 5, 5, 1};
	MLP myMLP(shape, 8);
    SGD mySGD(10, 0.00001, LOSS);
	//SGD mySGD(0.000001, 0.0000, LOSS);

	Eigen::VectorXd Xs[20000];
	Eigen::VectorXd Ys[20000];
	srand(65472);

	for (int i = 0; i < 20000; i++) {
		Xs[i] = Eigen::VectorXd::Random(5);
		Ys[i] = fuc_65472(Xs[i]);
	}

	myMLP.train<SGD>(100, mySGD, 90, 0, LOSS, Xs, Ys, 5000, abs_acc, nullptr);

	print(myMLP);
}

void verify_general_approach2()
{
	int shape[2] {3, 1};
	MLP myMLP(shape, 2);
	SGD mySGD(0.01, 0.00, LOSS);

	Eigen::VectorXd Xs[20000];
	Eigen::VectorXd Ys[20000];
	srand(65472);

	for (int i = 0; i < 20000; i++) {
		Xs[i] = Eigen::VectorXd::Random(3);
		Ys[i] = fuc_two(Xs[i]);
	}

	myMLP.train<SGD>(10, mySGD, 90, 0, LOSS, Xs, Ys, 10000, abs_acc, nullptr);

	print(myMLP);
}


int main()
{
	//verify_general_approach();
	verify_general_approach2();
	//sgd_test();
	//double_eq();
	//eigen_test1();
	//eigen_test2();
	//layer_test();
	//forward_test();
	//test_attr();

	system("pause");
	return 0;
}
